#include "gtest/gtest.h"
//1. Status  &&  2. SubcodeStatus && 3. ErrnoStatus

//1. Status
//	1.1 ctor
//		=> (code, state)
//		=> (code, nullptr)
//	1.2 virtual const char * getCodeState(code) const ;
//	1.3 virtual what() =>  std::string = [code] + ": " + state;
//2. SubcodeStatus: 
//	2.1 ctor 
//      =>Status(code, state), subcode(code)
//      =>Status(code), subcode(code)
//	2.2 virtual const char * getSubcodeState(subcode) const;
//	2.3 virtual what() =>  std::string = [code] + ": " + state + ": " + [subcode];
//3. ErrnoStatus: 
//	3.1 ctor
//		=> Status(code, state), errno(errno)
//		=> Status(code), errno(errno)
//	3.2 virtual const char * getErrnoState(errno) const ;
//	3.3 virtual what() => std::string = [code] + ": " + state + ": " + [errno]

#include "recovery/Status.h"
#include "recovery/SubcodeStatus.h"
#include "recovery/ErrnoStatus.h"

using namespace recovery;

class GenericStatusTest : public testing::Test {
public:
	/*  
	template<typename T>
	void _testMain(void){
        auto sr = T::OK();
		ASSERT_TRUE(sr);
		ASSERT_EQ(sr.what(), " OK ");
	}*/
	template<typename T>
	void _testCopyCtor(void){
		{
			auto ss = T::OK();
			T two(ss);
			ASSERT_TRUE(ss && two);
		}
		{
			auto ss = T::OK();
			T two = ss;
			ASSERT_TRUE(ss && two);
		}
	}
	template<typename T>
	void _testAssign(void){
		{
			auto one = T::OK();
			T two = T::OK();
			two = one;
			ASSERT_TRUE(one && two);
			ASSERT_EQ(two.what(), " OK ");
		}
		{
			auto one = T::OK();
			//one = one; //-Wself-assign-overloaded
			ASSERT_TRUE(one);
		}
	}
	template<typename T>
	void _testEqual(void){
		auto ss = T::OK();
		auto two = T::OK();
		ASSERT_TRUE(ss == two);
	}
	template<typename T>
	void _testMoveCtor(void){
		{
			auto two(std::move(T::OK()));
			ASSERT_TRUE(two);	
		}
		{
			auto one = T::OK();
			auto two = std::move(one);
			ASSERT_TRUE(one && two);
		}
	}
	template<typename T>
	void _testMoveAssign(void){
		{
			auto one = T::OK();
			auto two = T::OK();
			two = std::move(one);
			ASSERT_TRUE(one && two);
		}
		{
			auto one = T::OK();
			one = std::move(one);
			ASSERT_TRUE(one);
		}
	}
};

TEST_F(GenericStatusTest, testMain){
	auto _testMain = [](auto status){
		ASSERT_TRUE(status);
		ASSERT_EQ(status.what(), " OK ");
	};

	_testMain(Status::OK());
	_testMain(SubcodeStatus::OK());
	_testMain(ErrnoStatus::OK());
}
TEST_F(GenericStatusTest, testCopyCtor){
	_testCopyCtor<Status>();
	_testCopyCtor<SubcodeStatus>();
	_testCopyCtor<ErrnoStatus>();
}
TEST_F(GenericStatusTest, testAssign){
	_testAssign<Status>();
	_testAssign<SubcodeStatus>();
	_testAssign<ErrnoStatus>();
}
TEST_F(GenericStatusTest, testEqual){
	_testEqual<Status>();
	_testEqual<SubcodeStatus>();
	_testEqual<ErrnoStatus>();
}
TEST_F(GenericStatusTest, testMoveCtor){
	_testMoveCtor<Status>();
	_testMoveCtor<SubcodeStatus>();
	_testMoveCtor<ErrnoStatus>();
}
TEST_F(GenericStatusTest, testMoveAssign){
	_testMoveAssign<Status>();
	_testMoveAssign<SubcodeStatus>();
	_testMoveAssign<ErrnoStatus>();
}


TEST(StatusTest, testMain){
	auto err = Status::InvalidArgument("1st is null");
	ASSERT_TRUE(!err);

	auto s = Status::OK();
	ASSERT_TRUE(s != err);

	
	auto cmp = [](auto & new_s, const auto & s) -> bool{
		new_s = s.toStatus();	
		std::cout << "cmp:" << new_s.what() << std::endl;
		return (new_s == s);
	};

	auto new_s = Status::OK();
	ASSERT_TRUE(cmp(new_s, err));
	ASSERT_TRUE(cmp(new_s, s));
}
TEST(SubcodeStatusTest, testMain){
	auto err = SubcodeStatus::Invalid("invalid operation");
	ASSERT_TRUE(!err);

	auto s = SubcodeStatus::OK();
	ASSERT_TRUE(err != s);

	auto cmp = [](auto & new_s, const auto & s) -> bool{
		new_s = std::move(s.toStatus());	
		std::cout << "cmp:" << new_s.what() << std::endl;
		return (new_s == s);
	};

	auto new_s = Status::OK();
	ASSERT_TRUE(cmp(new_s, err));
	ASSERT_TRUE(cmp(new_s, s));
}
TEST(ErrnoStatusTest, testMain){
	auto err = ErrnoStatus::Error(2, "file not exist");
	ASSERT_TRUE(!err);

    auto s = ErrnoStatus::OK();
	ASSERT_TRUE(err != s);

	auto err1 = ErrnoStatus::Error(10000);
	ASSERT_TRUE(!err1);

	auto cmp = [](auto & new_s, const auto & s) -> bool{
		new_s = s.toStatus();	
		std::cout << "cmp:" << new_s.what() << std::endl;
		return (new_s == s);
	};

	auto new_s = Status::OK();
	ASSERT_TRUE(cmp(new_s, err));
	ASSERT_TRUE(cmp(new_s, s));
	ASSERT_TRUE(cmp(new_s, err1));
}
